﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Xml;

namespace wowSteadNotification
{
    class wsn_BL
    {
        // Internal props
        private List <Exception> errors;
        private XmlDocument forumLatestPosts;
        private string wsCharId;
        private string guildName;
        private Uri wsURLForum;
        private Uri wsURLLogout;
        private DataTable dtForumPostsLast;
        private DataTable dtForumPostsNew;
        private DataTable dtForumPosts;
        private bool lastReadSucceeded;
        private bool lastWriteSucceeded;
        private bool newReadSucceeded;

        // External props
        public bool LastReadSucceeded
        {
            get { return lastReadSucceeded; }
        }

        public bool LastWriteSucceeded
        {
            get { return lastWriteSucceeded; }
        }

        public bool NewReadSucceeded
        {
            get { return newReadSucceeded; }
        }

        public DataTable ForumPosts
        {
            get { return dtForumPosts; }
        }

        public List <Exception> Errors
        {
            get { return errors; }
        }

        /// <summary>
        /// The wowSteadNotification business logic constructor creates the request and
        /// parsing object to recieve the recent posts and determine the ammount of new posts.
        /// </summary>
        /// <param name="URL">URL to WowStead guild homepage as string to query the recent posts page.</param>
        /// <param name="charId">Character ID to authenticate as WowStead character.</param>
        /// <param name="passwordHash">Passwort Hash to authenticate as WowStead user.</param>
        /// <param name="userId">User ID to authenticate as WowStead user.</param>
        /// <param name="lastVisit">Last visit as unix timestamp.</param>
        public wsn_BL(string URL, string charId, string passwordHash, string userId, string lastVisit)
        {
            lastReadSucceeded = false;
            lastWriteSucceeded = false;
            newReadSucceeded = false;
            errors = new List<Exception>();
            try
            {
                wsURLForum = new Uri(URL + "/forums/latest");
                wsURLLogout = new Uri(URL + "/user/logout");
                guildName = wsURLForum.DnsSafeHost.Substring(0, wsURLForum.DnsSafeHost.IndexOf("."));

                Cookie keksCid = new Cookie("charid", charId, "/", wsURLForum.DnsSafeHost);
                Cookie keksPwd = new Cookie("password", passwordHash, "/", wsURLForum.DnsSafeHost);
                Cookie keksUid = new Cookie("userid", userId, "/", wsURLForum.DnsSafeHost);
                Cookie keksLvi = new Cookie("lastvisit", lastVisit, "/", wsURLForum.DnsSafeHost);
                wsCharId = charId;

                CookieContainer kekse = new CookieContainer();
                kekse.Add(keksCid);
                kekse.Add(keksPwd);
                kekse.Add(keksUid);
                kekse.Add(keksLvi);

                forumLatestPosts = getForumLatestPosts(kekse);
                if (forumLatestPosts != null)
                {
                    try 
                    {
                        dtForumPostsLast = new DataTable("Posts");
                        dtForumPostsLast.ReadXml("ForumPostsLast.xml");
                        lastReadSucceeded = true;
                    }
                    catch (Exception ex) 
                    { 
                        errors.Add(ex);
                        lastReadSucceeded = false;
                    }
                    dtForumPostsNew = createPostsDataTable();
                    if (dtForumPostsNew != null && dtForumPostsNew.Rows.Count > 0)
                    {
                        newReadSucceeded = true;
                        try
                        {
                            dtForumPostsNew.WriteXml("ForumPostsLast.xml", XmlWriteMode.WriteSchema);
                            lastWriteSucceeded = true;
                        }
                        catch (Exception ex)
                        {
                            errors.Add(ex);
                            lastWriteSucceeded = false;
                        }
                    }
                    if (dtForumPostsLast != null && dtForumPostsNew != null)
                        dtForumPosts = compareDataTables(dtForumPostsNew, dtForumPostsLast);
                    else
                        dtForumPosts = null;
                }
            }
            catch (Exception ex)
            {
                errors.Add(ex);
            }
        }

        /// <summary>
        /// Gets XML document containing the latest posts.
        /// </summary>
        /// <param name="kekse">Container of cookies for auth.</param>
        /// <returns>XML document containing </returns>
        private XmlDocument getForumLatestPosts(CookieContainer kekse)
        {
            string strLatestPosts;
            XmlDocument xdoc;
            try
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(wsURLForum);
                request.CookieContainer = kekse;
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();

                Stream responseStream = response.GetResponseStream();
                string responseString = new StreamReader(responseStream, System.Text.Encoding.UTF8).ReadToEnd();

                // Get content recent posts by splitting reponse string by table tags
                int strPosStart = responseString.IndexOf("<table class=\"forumblock\" id=\"latestposts\">");
                strLatestPosts = responseString.Substring(strPosStart);
                int strPosEnd = strLatestPosts.IndexOf("</table>");
                strLatestPosts = strLatestPosts.Remove(strPosEnd + 8);

                // Create XmlDocument from recent posts block
                xdoc = new XmlDocument();
                xdoc.LoadXml(strLatestPosts);

                // Close streams and logout wowstead
                response.Close();
                responseStream.Close();
                request = (HttpWebRequest)WebRequest.Create(wsURLLogout);
                request.CookieContainer = kekse;
                response = (HttpWebResponse)request.GetResponse();
                return xdoc;
            }
            catch (Exception ex)
            {
                errors.Add(ex);
                return null; 
            }
        }

        private DataTable createPostsDataTable()
        {
            DataTable dtPosts = new DataTable("Posts");
            DataColumn dc;
            DataRow dr;

            // Column title and add to DataTable.
            dc = new DataColumn();
            dc.DataType = System.Type.GetType("System.String");
            dc.ColumnName = "Post_Title";
            dtPosts.Columns.Add(dc);
            // Column title and add to DataTable.
            dc = new DataColumn();
            dc.DataType = System.Type.GetType("System.String");
            dc.ColumnName = "Post_Link";
            dtPosts.Columns.Add(dc);
            // Column title and add to DataTable.
            dc = new DataColumn();
            dc.DataType = System.Type.GetType("System.String");
            dc.ColumnName = "Forum";
            dtPosts.Columns.Add(dc);
            // Column Poster and add to DataTable.
            dc = new DataColumn();
            dc.DataType = System.Type.GetType("System.String");
            dc.ColumnName = "Poster";
            dtPosts.Columns.Add(dc);
            // Column Age and add to DataTable.
            dc = new DataColumn();
            dc.DataType = System.Type.GetType("System.DateTime");
            dc.ColumnName = "Age";
            dtPosts.Columns.Add(dc);

            XmlNodeList xnodes = forumLatestPosts.GetElementsByTagName("tr");
            foreach (XmlNode xnode in xnodes)
            {
                try
                {
                    dr = dtPosts.NewRow();
                    dr["Post_Title"] = xnode.ChildNodes[0].ChildNodes[0].InnerText;
                    dr["Post_Link"] = wsURLForum.Scheme + "://" + wsURLForum.DnsSafeHost + xnode.ChildNodes[0].ChildNodes[0].ChildNodes[0].Attributes[0].Value;
                    dr["Forum"] = xnode.ChildNodes[1].InnerText;
                    dr["Poster"] = xnode.ChildNodes[2].ChildNodes[1].InnerText;
                    string [] tmpAge = xnode.ChildNodes[3].InnerText.Split(' ');
                    int hours;
                    switch (tmpAge[1])
                    {
                        case "hours":
                        case "hour":
                            hours = Convert.ToInt16(tmpAge[0]);
                            break;
                        case "day":
                        case "days":
                            hours = Convert.ToInt16(tmpAge[0]) * 24;
                            break;
                        case "week":
                        case "weeks":
                            hours = Convert.ToInt16(tmpAge[0]) * 168;
                            break;
                        case "month":
                        case "months":
                            hours = Convert.ToInt16(tmpAge[0]) * 744;
                            break;
                        case "year":
                        case "years":
                            hours = Convert.ToInt16(tmpAge[0]) * 8760;
                            break;
                        default:
                            hours = 0;
                            break;
                    }
                    TimeSpan ts = new TimeSpan(hours, 0, 0);
                    dr["Age"] = DateTime.Now.Subtract(ts);
                    dtPosts.Rows.Add(dr);
                }
                catch { };
            }
            return dtPosts;
        }

        private DataTable compareDataTables(DataTable First, DataTable Second)
        {
            First.TableName = "FirstTable";
            Second.TableName = "SecondTable";

            //Create Empty Table
            DataTable table = new DataTable("Difference");

            try
            {
                //Must use a Dataset to make use of a DataRelation object
                using (DataSet ds = new DataSet())
                {
                    //Add tables
                    ds.Tables.AddRange(new DataTable[] { First.Copy(), Second.Copy() });

                    //Get Columns for DataRelation
                    DataColumn[] firstcolumns = new DataColumn[ds.Tables[0].Columns.Count-1];

                    for (int i = 0; i < firstcolumns.Length; i++)
                    {
                        firstcolumns[i] = ds.Tables[0].Columns[i];
                    }

                    DataColumn[] secondcolumns = new DataColumn[ds.Tables[1].Columns.Count-1];

                    for (int i = 0; i < secondcolumns.Length; i++)
                    {
                        secondcolumns[i] = ds.Tables[1].Columns[i];
                    }

                    //Create DataRelation
                    DataRelation r = new DataRelation(string.Empty, firstcolumns, secondcolumns, false);
                    ds.Relations.Add(r);

                    //Create columns for return table
                    for (int i = 0; i < First.Columns.Count; i++)
                    {
                        table.Columns.Add(First.Columns[i].ColumnName, First.Columns[i].DataType);
                    }

                    //If First Row not in Second, Add to return table.
                    table.BeginLoadData();

                    foreach (DataRow parentrow in ds.Tables[0].Rows)
                    {
                        DataRow[] childrows = parentrow.GetChildRows(r);
                        if (childrows == null || childrows.Length == 0)
                            table.LoadDataRow(parentrow.ItemArray, true);
                    }
                    table.EndLoadData();
                }
            }
            catch (Exception ex)
            {
                errors.Add(ex);
            }

            return table;
        }

        /// <summary>
        /// This method creates a RSS 2.0 feed at the given path.
        /// </summary>
        /// <param name="path">Path name for saving the xml file containing the feed.</param>
        /// <param name="authorMail">E-Mail address for the copyright-tag (author).</param>
        /// <returns></returns>
        public bool createRSS(string path, string authorMail)
        {
            try
            {
                XmlTextWriter xtwFeed = new XmlTextWriter(path, System.Text.Encoding.UTF8);
                xtwFeed.WriteStartDocument();

                // The mandatory rss tag
                xtwFeed.WriteStartElement("rss");
                xtwFeed.WriteAttributeString("version", "2.0");

                // The channel tag contains RSS feed details
                xtwFeed.WriteStartElement("channel");
                xtwFeed.WriteElementString("title", guildName + " RSS Feed");
                xtwFeed.WriteElementString("link", wsURLForum.AbsoluteUri);
                xtwFeed.WriteElementString("description", "The latests posts within forum.");
                xtwFeed.WriteElementString("copyright", guildName + ", " + authorMail);
                xtwFeed.WriteElementString("pubDate", DateTime.Now.ToString("ddd, dd MMM yyy HH:mm:ss zzz"));
                xtwFeed.WriteStartElement("image");
                xtwFeed.WriteElementString("url", "http://www.wowstead.com/images/avatar.php?" + wsCharId);
                xtwFeed.WriteElementString("title", "Brought to you by...");
                xtwFeed.WriteElementString("link", "http://www.wowstead.com/character/" + wsCharId);
                xtwFeed.WriteEndElement();

                // Loop through the content of the database and add them to the RSS feed
                foreach (DataRow dr in dtForumPostsNew.Rows)
                {
                    xtwFeed.WriteStartElement("item");
                    xtwFeed.WriteElementString("title", dr[3].ToString() + " in " + dr[2].ToString());
                    xtwFeed.WriteElementString("description", dr[0].ToString());
                    xtwFeed.WriteElementString("link", dr[1].ToString());
                    xtwFeed.WriteElementString("pubDate", ((DateTime)dr[4]).ToString("ddd, dd MMM yyy HH:mm:ss zzz"));
                    xtwFeed.WriteEndElement();
                }

                // Close all tags
                xtwFeed.WriteEndElement();
                xtwFeed.WriteEndElement();
                xtwFeed.WriteEndDocument();
                xtwFeed.Flush();
                xtwFeed.Close();
                return true;
            }
            catch (Exception ex)
            {
                errors.Add(ex);
                return false;
            }
        }

        /// <summary>
        /// This method sends a mail containing a notification for latest posts.
        /// </summary>
        /// <param name="mailSubject">Mail subject.</param>
        /// <param name="mailBody">Text above the list of posts.</param>
        /// <param name="mailTrailer">Text below the list of posts.</param>
        /// <param name="mailFrom">Single e-mail address of sender (will be used as MailTo too).</param>
        /// <param name="mailRecipients">Array of e-mail addresses of recipients (Bcc).</param>
        /// <param name="smtpSever">Hostname of smtp server.</param>
        /// <returns></returns>
        public bool mailNotifications(string eMailSubject, string eMailBody,
            string eMailTrailer, string eMailFrom, System.Collections.Specialized.StringCollection eMailRecipients, string smtpSever)
        {
            try
            {
                foreach (DataRow dr in ForumPosts.Rows)
                {
                    eMailBody += "    " + dr[3].ToString() + ": \"" + dr[0].ToString() + "\" (" + ((DateTime)dr[4]).ToShortDateString() + ")\n";
                    eMailBody += "    ---> " + dr[1].ToString() + "\n";
                }
                eMailBody += "\n" + eMailTrailer;

                MailMessage mail = new MailMessage();
                mail.From = new MailAddress(eMailFrom);
                mail.To.Add(eMailFrom);
                foreach (string address in eMailRecipients)
                    mail.Bcc.Add(address);
                mail.Subject = eMailSubject + guildName + "-Forum";
                mail.Body = eMailBody;

                SmtpClient smtpMail = new SmtpClient(smtpSever);
                smtpMail.Send(mail);
                return true;
            }
            catch (Exception ex)
            {
                errors.Add(ex);
                return false;
            }
        }

    }
}
